#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 19 23:41:57 2018
@project: 天池比赛-A股主板上市公司公告信息抽取
@group: MZH_314
@author: LHQ
"""
import re
from itertools import groupby
from collections import namedtuple


Entity = namedtuple('Entity', ['index', "entity", 'tag'])


class Recognizer:
    """识别器抽象类
    提供一个recognize方法供外部调用，返回句子里的实体（列表）

    Attributes
    ----------
    TAG : str
        标签名称，即识别器识别到的实体的类别标签
        该属性需由子类添加(类属性或对象属性都可以)
    """
    def recognize_entity_span(self, sentence):
        """识别实体在句子中的索引位置

        该方法需要由子类实现

        Args
        ----
        sentence : str
            待从中识别实体的句子

        Yields
        ------
        span : tuple, 结构-->(start, end)
            实体在sentence中始末位置
        """
        raise NotImplementedError


    def recognize(self, sentence):
        """实体识别方法

        Args
        ----
        sentence : str
            待从中识别实体的句子

        Returns
        -------
        entities : iterable
            识别到的实体, 每个实体为命名元组Entity实例
        """
        entities = []
        for start, end in self.recognize_entity_span(sentence):
            ent = sentence[start:end]
            entity = Entity(index=(start, end), entity=ent, tag=self.TAG)
            entities.append(entity)
        return entities
       

class ReRecognizer(Recognizer):
    """正则识别器
    加入实体的正则表达式， 找到句子中符合该正则表达式的实体
    """
    def __init__(self):
        self.patterns = []
        self.cache_patterns = set()

    def add_pattern(self, regexp):
        """添加实体的正则模式

        Args
        ----
        regexp : str
             实体的正则表达式
        """
        if regexp not in self.cache_patterns:
            self.cache_patterns.add(regexp)
            p = re.compile(regexp)
            self.patterns.append(p)

    def recognize_entity_span(self, sentence):
        """返回实体在句子中的的索引位置

        Args
        ----
        sentence : str
            待提取实体的句子

        Yields
        ------
        span : 二元tuple, 为实体的起止索引位置 
        """
        """识别实体在句子中的索引位置
        """
        spans_all = []
        for p in self.patterns:
            for m in p.finditer(sentence):
                spans_all.append(m.span())
        # 同一个起始位置如果有多个实体，则选择结束位置最大的那个
        spans_max_end = []
        for k, spans in groupby(sorted(spans_all), lambda x: x[0]):
            spans_max_end.append(max(spans))
        # 同一个结束位置如果有多个实体，则选择起始位置最小的那个
        for k, spans in groupby(sorted(spans_max_end, key=lambda x: x[1]), lambda x: x[1]):
            start, end = min(spans)
            yield start, end


class DateRecognizer(ReRecognizer):
    """
    日期识别器
    """

    TAG = 'date'
    
#    def __init__(self):
#        self.patterns = []
#        
#    def add_pattern(self, regexp):
#        p = re.compile(regexp)
#        self.patterns.append(p)
#        
#    def recognize_entity_span(self, sentence):
#        """识别实体在句子中的索引位置
#        """
#        spans_all = []
#        for p in self.patterns:
#            for m in p.finditer(sentence):
#                spans_all.append(m.span())
#        # 同一个起始位置如果有多个实体，则选择结束位置最大的那个
#        spans_max_end = []
#        for k, spans in groupby(sorted(spans_all), lambda x: x[0]):
#            spans_max_end.append(max(spans))
#        # 同一个结束位置如果有多个实体，则选择起始位置最小的那个
#        for k, spans in groupby(sorted(spans_max_end, key=lambda x: x[1]), lambda x: x[1]):
#            start, end = min(spans)
#            yield start, end
#        
    @classmethod
    def of_default(cls):
        """类方法提供实例化，附带默认的日期正则
        """
        recognizer = cls()
        recognizer.add_pattern("\d+年\d+月\d+日至\d+月\d+日")
        recognizer.add_pattern("\d+年\d+月\d+日至\d+年\d+月\d+日")
        recognizer.add_pattern("\d+年\d+月\d+日")
        recognizer.add_pattern("\d+-\d+-\d+")
        recognizer.add_pattern("\d+-\d+-\d+至\d+-\d+")
        recognizer.add_pattern("\d+-\d+-\d+至\d+-\d+-\d+")
        return recognizer


class StockQuantityRecognizer(Recognizer):
    """
    股数识别器
    """

    TAG = "stockquantity"
    
    def recognize_entity_span(self, sentence):
        for m in re.finditer("[\d,]+[\s万]*股", sentence):
            start, end = m.span()
            yield start, end
    

class PercentRecognizer(Recognizer):
    """
    百分比识别器
    """
    TAG = "percent"
    
    def recognize_entity_span(self, sentence):
        for m in re.finditer("占[\w\s]*?([\d\.]+%?)", sentence):
            start, end = m.span(1)
            yield start, end

class AmountRecognizer(Recognizer):
    """
    金额识别器
    """
    TAG = "amount"
    
    def recognize_entity_span(self, sentence):
        for m in re.finditer("[\d,]+[\s万]*元", sentence):
            start, end = m.span()
            yield start, end

        
def make_recognizer(recognizer_name):
    """简单工厂方法生成对象
    """
    recognizers = {
            "re": ReRecognizer,
            "date": DateRecognizer.of_default,
            "stockquantity": StockQuantityRecognizer,
            "percent": PercentRecognizer,
            "amount": AmountRecognizer,
            }
    recognizer = recognizers[recognizer_name]
    return recognizer()
    
        
